package sweet.helper.cli

/** Similar to scala.Application, but with better option parsing and args support. 
 * You may mix/entends this trait with #run method and get immediately access to args and opts variables.
 * For example: <pre><code>
 *    object MyApp extends sweet.helper.cli.CliApp{
 *      def run{
 *        if(opts.contains("h")){ println("scala MyApp [options] arguments."); exit(1) }
 *        
 *        for(x <- args) println("Argument " + x)
 *        for((k,v) <- opts) println("Options " + k + ": " + v)
 *      }
 *    }
 * </code></pre>
 * You may now test your MyApp like this:
 * <pre>$ sweet/bin/run.sh MyApp</pre>
 * <pre>$ sweet/bin/run.sh MyApp -h</pre>
 * <pre>$ sweet/bin/run.sh MyApp one two three</pre>
 * <pre>$ sweet/bin/run.sh MyApp -v -f -b one two three</pre>
 */
trait CliApp {
  /** Holds the new argument list after parseOpt */
  var args: List[String] = _
  
  /** Holds the options map after parseOpt */
  var opts: scala.collection.Map[String, String] = _
  
  /** default implementation that will parse argv and initialize new args and opts.*/
  def main(argv: Array[String]){
    val res = parseOpts(argv)
    args = res._1
    opts = res._2
    run
  }
  
  /** Subclass need to implement this to get access to args and opts. */
  def run: Unit
  
  /** Parse a argument array to pick up options flags. The left over argument are stored and 
   * return as new arg list. Options can be prefix with either "-" or "--". An option is a single
   * string line without space! You may specify parameter to option with an "=" sign. */
  def parseOpts(argv: Array[String]): (List[String], scala.collection.Map[String, String]) = {
    val opts = new scala.collection.mutable.HashMap[String, String]
    val args = new scala.collection.mutable.ListBuffer[String]
    
    def addArgOpt(arg: String) = { 
      val pos = arg.indexOf("=")
      if(pos <0) opts += (arg -> "true")
      else opts += (arg.substring(0, pos) -> arg.substring(pos+1))
    }
    
    for(arg <- argv){
      if(arg.startsWith("--")) addArgOpt(arg.substring(2))
      else if(arg.startsWith("-")) addArgOpt(arg.substring(1))
      else args += arg
    }
    (args.readOnly.toList, opts.readOnly)
  }
}
